# -*- coding: utf-8 -*-

"""
正则表达式(Regex) - Python `re` 模块实践与拓展代码

本代码旨在作为您学习正则表达式的交互式实验平台。
它全面地演示了基于先前文档总结的 `re` 模块核心功能。
每个函数专注于一个特定的知识点，并附有详细解释。

您可以直接运行此文件，查看所有预设的测试结果。
更重要的是，您可以在每个函数末尾的“思考拓展区”或最后的“自定义测试区”
添加您自己的测试用例，将理论知识转化为实践技能。
"""

import re

def test_basic_match_vs_search():
    """
    演示 `re.match()` 和 `re.search()` 的核心区别。
    - `re.match()`: 仅从字符串的开头开始匹配。
    - `re.search()`: 扫描整个字符串，返回第一个匹配项。
    """
    print("\n" + "="*20 + " 1. `re.match` vs `re.search` " + "="*20)
    text = "The user's email is example@example.com, please contact him."
    # pattern = r"email"

    pattern = r"The user's"

    print(f"文本: '{text}'")
    print(f"模式: '{pattern}'")

    # 使用 re.match()
    match_obj = re.match(pattern, text)
    print(f"\n- re.match() 结果: {match_obj}")
    if match_obj:
        print(f"  > 匹配成功！匹配内容: '{match_obj.group(0)}', 位置: {match_obj.span()}")
    else:
        print("  > 匹配失败！因为 'email' 不在文本的开头。")

    # 使用 re.search()
    search_obj = re.search(pattern, text)
    print(f"\n- re.search() 结果: {search_obj}")
    if search_obj:
        print(f"  > 匹配成功！匹配内容: '{search_obj.group(0)}', 位置: {search_obj.span()}")
    else:
        print("  > 匹配失败！")

    # --- 你的拓展思考区 ---
    # 尝试修改 text 和 pattern，观察二者结果的变化。
    # 例如，如果 `text = "email is important"`，结果会怎样？



def test_findall_vs_finditer():
    """
    演示 `re.findall()` 和 `re.finditer()` 的区别。
    - `re.findall()`: 返回一个包含所有匹配字符串的【列表】。
    - `re.finditer()`: 返回一个【迭代器】，每个元素是一个匹配对象(MatchObject)，更节省内存。
    """
    print("\n" + "="*20 + " 2. `re.findall` vs `re.finditer` " + "="*20)
    text = "商品A价格: 199元, 商品B价格: 288元, 商品C价格: 50元"
    pattern = r"(\d+)元" # \d+ 匹配一个或多个数字

    print(f"文本: '{text}'")
    print(f"模式: '{pattern}'")

    # 使用 re.findall()
    # 当模式中有捕获组时，findall只返回捕获组的内容
    findall_result = re.findall(pattern, text)
    print(f"\n- re.findall() 结果: {findall_result}")
    print("  > findall 直接返回了所有匹配到的价格数字列表。")

    # 使用 re.finditer()
    finditer_result = re.finditer(pattern, text)
    print(f"\n- re.finditer() 结果: {finditer_result}")
    print("  > finditer 返回一个迭代器，我们需要遍历它来获取每个匹配的详细信息：")
    for match in finditer_result:
        # match.group(0) 是整个匹配的内容 ("199元")
        # match.group(1) 是第一个捕获组的内容 ("199")
        print(f"    - 完整匹配: '{match.group(0)}', 捕获的价格: '{match.group(1)}', 位置: {match.span()}")

    # --- 你的拓展思考区 ---
    # 如果模式改成 r"\d+元" (没有捕获组)，findall的结果会是什么？
    # 尝试在一个非常大的文本文件中查找所有匹配项，感受 finditer 的内存优势。

def test_sub_and_split():
    """
    演示 `re.sub()` (替换) 和 `re.split()` (分割) 的用法。
    """
    print("\n" + "="*20 + " 3. `re.sub` (替换) 和 `re.split` (分割) " + "="*20)

    # --- re.sub() 演示 ---
    text1 = "联系方式：138-1234-5678, 请勿泄露。"
    # 隐藏手机号中间四位
    sub_result = re.sub(r"(\d{3})-\d{4}-(\d{4})", r"\1-****-\2", text1)
    print(f"\n- re.sub() 简单替换:")
    print(f"  原始文本: '{text1}'")
    print(f"  替换结果: '{sub_result}'")

    # `repl` 参数使用函数
    def price_doubler(match):
        """一个函数，将匹配到的数字字符串翻倍后返回"""
        price = int(match.group(1))
        return str(price * 2) + "元"

    text2 = "A商品50元, B商品120元"
    sub_func_result = re.sub(r"(\d+)元", price_doubler, text2)
    print(f"\n- re.sub() 使用函数替换:")
    print(f"  原始文本: '{text2}'")
    print(f"  价格翻倍后: '{sub_func_result}'")


    # --- re.split() 演示 ---
    text3 = "apple,banana; a cherry. and an orange"
    # 使用非字母数字字符进行分割
    split_result = re.split(r"[^\w]+", text3)
    print(f"\n- re.split() 分割:")
    print(f"  原始文本: '{text3}'")
    print(f"  分割结果: {split_result}")

    # --- 你的拓展思考区 ---
    # 尝试写一个 re.sub，将所有 "cat" 替换为 "dog"，但 "category" 中的 "cat" 不替换。
    text4 = "cat,category,hello cat"
    sub_result = re.sub(r"cat(?!egory)", r"dog", text4)
    print(sub_result)
    # (提示: 需要用到单词边界 \b)
    # 尝试使用 re.split 保留分隔符。 (提示: 在模式中使用捕获组)

def test_compile_and_flags():
    """
    演示 `re.compile()` (预编译) 和 flags (标志位) 的重要性。
    - `re.compile()`: 提高重复使用同一模式时的性能。
    - `flags`: 控制匹配行为，如忽略大小写(re.I), 多行匹配(re.M)。
    """
    print("\n" + "="*20 + " 4. `re.compile` (编译) 和 `flags` (标志位) " + "="*20)

    # 预编译一个忽略大小写的模式
    pattern = re.compile(r"error", re.I) # re.I 是 re.IGNORECASE 的缩写
    print(f"编译了一个忽略大小写的模式: '{pattern.pattern}'")

    logs = [
        "2025-07-28: All systems running.",
        "2025-07-28: Minor warning detected.",
        "2025-07-28: Critical ERROR occurred!",
        "2025-07-28: An error was resolved.",
    ]

    print("\n- 搜索日志中的 'error' (不区分大小写):")
    for log_entry in logs:
        match = pattern.search(log_entry)
        if match:
            print(f"  > 在 '{log_entry}' 中找到匹配: '{match.group(0)}'")

    # 多行模式 (re.M) 演示
    multiline_text = "line1: value1\nline2: value2\nline3: value1"
    # ^ 默认只匹配整个字符串的开头
    # 使用 re.M 后，^ 也能匹配每一行的开头
    multiline_pattern = re.compile(r"^line\d", re.M)
    multiline_results = multiline_pattern.findall(multiline_text)
    print("\n- 多行模式(re.M)演示:")
    print(f"  文本:\n'''\n{multiline_text}\n'''")
    print(f"  使用 '^line\\d' 和 re.M 查找到所有行首匹配: {multiline_results}")

    # --- 你的拓展思考区 ---
    # 尝试组合多个标志位，例如 re.I | re.M。
    # 使用 re.X (VERBOSE) 标志位来为一个复杂的正则表达式编写注释。

def test_advanced_grouping_and_lookaround():
    """
    演示高级功能：命名捕获组和零宽度断言(Lookaround)。
    - 命名捕获组: `(?P<name>...)` 使代码更具可读性。
    - 后行断言: `(?<=...)` 匹配某个模式之前的位置，但不包含该模式。
    """
    print("\n" + "="*20 + " 5. 高级分组与断言 " + "="*20)
    text = "User: John Doe, ID: 12345, Role: Admin; User: Jane Smith, ID: 67890, Role: Member"

    # 使用命名捕获组提取用户信息
    pattern_named = re.compile(r"User: (?P<name>[\w\s]+), ID: (?P<id>\d+), Role: (?P<role>\w+)")
    print("- 使用命名捕获组:")
    for match in pattern_named.finditer(text):
        # 通过名称访问捕获组，更清晰
        print(f"  > 找到用户: 姓名='{match.group('name')}', ID='{match.group('id')}', 角色='{match.group('role')}'")

    # 使用后行断言提取特定角色的用户名
    # 目标：只提取角色为 "Admin" 的用户名
    pattern_lookbehind = r"(?<=Role: Admin; User: )[\w\s]+"
    # [原文模糊] 原文中的例子并不适合此场景，此处为根据概念进行的拓展演示
    text_admins = "Role: Member; User: Tom; Role: Admin; User: Alice; Role: Admin; User: Bob"
    admins = re.findall(pattern_lookbehind, text_admins)
    print("\n- 使用后行断言 `(?<=...)`:")
    print(f"  文本: '{text_admins}'")
    print(f"  模式: '{pattern_lookbehind}'")
    print(f"  > 仅提取角色为 'Admin' 的用户名: {admins}")
    print("  > 注意：'Role: Admin; User: ' 这部分内容被用作匹配条件，但并未包含在结果中。")

    # --- 你的拓展思考区 ---
    # 尝试使用先行断言 `(?=...)` 来匹配一个密码，要求密码必须包含数字。
    # 例如，匹配 `pass123` 但不匹配 `password`。
    # (提示: `^(?=.*\d).+$`)

def user_expansion_zone():
    """
    这是一个专为您准备的自定义测试区域。
    请在这里自由地编写您的测试代码。
    """
    print("\n" + "="*20 + " 6. 您的自定义测试区 " + "="*20)

    # 步骤1: 定义您想测试的文本
    my_text = "在这里输入您想分析的任何文本，例如一段HTML代码: <p>Hello World!</p>"

    # 步骤2: 编写您的正则表达式模式
    # 例如，我们尝试提取HTML标签的名称
    my_pattern = r"<(\w+)>"

    # 步骤3: 选择合适的 re 函数并执行
    matches = re.findall(my_pattern, my_text)

    # 步骤4: 打印和分析结果
    print(f"我的文本: '{my_text}'")
    print(f"我的模式: '{my_pattern}'")
    print(f"查找结果: {matches}")
    # 预计结果: ['p']

if __name__ == "__main__":
    # --- 依次执行所有测试函数 ---
    # test_basic_match_vs_search()
    # test_findall_vs_finditer()
    # test_sub_and_split()
    # test_compile_and_flags()
    # test_advanced_grouping_and_lookaround()

    # --- 运行您的自定义测试 ---
    user_expansion_zone()